/* NOTICE
    OpenJaWS - Open Java Weather Station
    
    Copyright (C) 2008 Grant Gardner <grant@lastweekend.com.au>
    
    OpenJaWS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
NOTICE */

package au.com.lastweekend.openjaws;

import java.io.IOException;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.com.lastweekend.openjaws.api.Readings;
import au.com.lastweekend.openjaws.api.WeatherStation;
import au.com.lastweekend.openjaws.api.WeatherStationException;
import au.com.lastweekend.ws2300.ForecastInfo;
import au.com.lastweekend.ws2300.SerialDevice;
import au.com.lastweekend.ws2300.WS2300;
import au.com.lastweekend.ws2300.WS2300.HistoryInfo;

/**
 * OpenJaWS implementation of WS2300.
 * 
 * For the most part we handle the IO Exceptions that can be thrown by the ws2300 device.
 * 
 * @author ggardner
 * 
 */
public class WS2300Station implements WeatherStation {

    private WS2300 ws2300;

    private static final Logger LOG = LoggerFactory.getLogger(WS2300Station.class);

    public WS2300Station(WS2300 ws2300) {

        this.ws2300 = ws2300;
    }

    public HistoryIterator historyIterator(final Date fromDate) throws WeatherStationException {

        final HistoryInfo histInfo;
        try {
            histInfo = ws2300.getHistoryInfo();
        } catch (IOException e1) {
            throw new WeatherStationException("IOException getting history info", e1);
        }

        return new HistoryIterator() {

            int currentRecord = histInfo.getIndex(fromDate);

            public boolean hasNext() {

                return currentRecord < histInfo.getNumRecords();
            }

            public void next(Readings readings) throws WeatherStationException {

                try {
                    histInfo.getRecord(currentRecord, readings);
                    currentRecord++;
                } catch (IOException e) {
                    throw new WeatherStationException("Failed reading history record " + currentRecord, e);
                }

            }
        };
    }

    public Date getCurrentTime() throws WeatherStationException {

        try {
            return ws2300.getCurrentTime();
        } catch (IOException e) {
            throw new WeatherStationException(e);
        }
    }

    public int getReadDelay() throws WeatherStationException {

        try {
            return ws2300.getSecondsToNextAvailableReading();
        } catch (IOException e) {
            throw new WeatherStationException(e);
        }
    }

    public void setCurrentTime(Date date) throws WeatherStationException {

        try {
            ws2300.setCurrentTime(date);
        } catch (IOException e) {
            throw new WeatherStationException(e);
        }

    }

    public void fetchReadings(Readings readings) {

        Date readingTime = new Date();
        try {
            readingTime = ws2300.getCurrentTime();
        } catch (IOException e) {
            LOG.warn("Reading time not available", e);
        }
        readings.setReadingTime(readingTime);

        try {
            ws2300.getRelativeAirPressure(readings.getAirPressure(), readingTime);
        } catch (IOException e) {
            LOG.warn("Air Pressure not available", e);
        }
        try {
            ws2300.getIndoorHumidity(readings.getIndoorHumidity(), readingTime);
        } catch (IOException e) {
            LOG.warn("Indoor Humidity not available", e);
        }
        try {
            ws2300.getOutdoorHumidity(readings.getOutdoorHumidity(), readingTime);
        } catch (IOException e) {
            LOG.warn("Outdoor Humidity not available", e);

        }
        try {
            ws2300.getIndoorTemperature(readings.getIndoorTemperature(), readingTime);
        } catch (IOException e) {
            LOG.warn("Indoor Temperature not available", e);

        }
        try {
            ws2300.getOutdoorTemperature(readings.getOutdoorTemperature(), readingTime);
        } catch (IOException e) {
            LOG.warn("Outdoor Temperature not available", e);
        }

        try {
            readings.setRainAbsolute(ws2300.getRainAbsolute());
        } catch (IOException e) {
            LOG.warn("Rain absolute not available", e);
        }

        try {
            ws2300.getRain1Hour(readings.getRain1H(), readingTime);
        } catch (IOException e) {
            LOG.warn("1 hour rainfall not available", e);
        }
        try {
            ws2300.getRain24Hours(readings.getRain24H(), readingTime);
        } catch (IOException e) {
            LOG.warn("24 hour rainfall not available", e);;
        }
        try {
            ws2300.getWindSpeed(readings.getWindSpeed(), readingTime);
        } catch (IOException e) {
            LOG.warn("Wind speed not available", e);
        }

        try {
            ForecastInfo forecastInfo = ws2300.getForecast();
            readings.putStationValue("Forecast", forecastInfo.getForecast());
            readings.putStationValue("Tendency", forecastInfo.getTendency());
        } catch (IOException e) {
            LOG.warn("Forecast not available", e);
        }

        try {
            double[] directions = ws2300.getWindDirections();
            readings.putStationValue("WindDirections", directions);
        } catch (IOException e) {
            LOG.warn("Wind directions not available", e);
        }

    }

    /**
     * Convenience method to output current readings to the logs
     * 
     */
    public void logReadings() {

        long now = System.currentTimeMillis();
        Readings readings = new Readings();
        fetchReadings(readings);
        LOG.info("\n" + readings.toString());

        now = System.currentTimeMillis() - now;
        LOG.info(String.format("Took readings in %dms", now));

        try {
            int nextReading = ws2300.getSecondsToNextAvailableReading();
            LOG.info(String.format("%ds to next reading", nextReading));
        } catch (IOException e) {
            LOG.warn("Unable to get time to next reading", e);
        }
    }

    /**
     * Convenience method to output history to the logs
     * 
     * @throws IOException
     */
    public void logHistory() throws IOException {

        Readings readings = new Readings();
        HistoryInfo histInfo = ws2300.getHistoryInfo();
        LOG.info(readings.format(true, false, false, false));
        int numRecords = histInfo.getNumRecords();
        for (int i = 0; i < numRecords; i++) {
            histInfo.getRecord(i, readings);
            LOG.info(readings.format(false, true, false, false));
        }

    }

    /**
     * Usage: ....openjaws.ws2300.WS2300 comPort [true|false - show history]
     * 
     * Writes current readings to the log, optionally will also dump history information.
     * 
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {

        if (args.length < 1) {
            System.err.println("Usage:");
            System.err.println("comPort [true|false - show History, default false]");
            return;
        }

        SerialDevice device = null;
        String comPort = args[0];
        device = new SerialDevice(comPort);
        try {
            WS2300Station ws2300 = new WS2300Station(new WS2300(device));

            if (args.length > 1 && Boolean.parseBoolean(args[1])) {
                ws2300.logHistory();
            }

            ws2300.logReadings();

        } finally {
            if (device != null) {
                device.close();
            }
        }

    }

}
